package Logica;

import java.util.Queue;

import General.Constantes;

/**
 * Es la clase principal de la logica del juego. Esta clase se encarga de que todos los objetos
 * esten en perfecta sincronizacion.
 * 
 * @author Mariano Rodecker
 * @author Matias Varela
 * 
 */
public class JuegoLogico {

	/* ################## ATRIBUTOS DE INSTANCIA ######################## */
	protected boolean press_up, press_down, press_right, press_left, press_z;
	protected MapaVisible mapaV;
	protected Mario mario;

	// estas variables indican la situacion actual del juego, es decir en que
	// estado se encuentra la ejecucion del juego logico.
	protected boolean juegoFinalizado, juegoPerdido, juegoGanado,juegoReintentar;

	/* VARIABLES AUXILIARES */
	private int dx;
	private hiloMario hm;
	private hiloObjetos ho;

	/* ################## CONSTRUCTORES ######################## */

	/**
	 * Constructor para un Juego Logico que recibe una matriz de celdas sobre la cual se va a
	 * desarrolar la ejecucion del juego. Este constructor inicializa todos los elementos necesarios
	 * para su ejecucion.
	 * 
	 * @param matriz
	 *            - matriz de celdas en la cual se desarrollara la ejecucion del juego logico.
	 */
	public JuegoLogico(Mario mario,MapaVisible mapaV) {
		this.mapaV=mapaV;
		this.mario=mario;

	}

	/* ################## COMANDOS ######################## */

	/**
	 * Inicia todos los hilos necesarios para correr el juego logico.
	 */
	public void correrJuego() {
		if (hm == null)
			hm = new hiloMario();

		hm.start();
		
		if (ho==null)
			ho=new hiloObjetos();
		ho.start();
	}

	/**
	 * Detiene todos los hilos.
	 */
	public void detener() {
		if (hm != null)
			hm.pararHilo();
		if (ho != null)
			ho.parar();

	}

	/* Comandos para las teclas */
	/**
	 * Establece si se ha presionado la tecla de arriba
	 * 
	 * @param p
	 */
	public void pressKey_up(boolean p) {
		press_up = p;
	}

	/**
	 * Establece si se ha presionado la tecla de abajo
	 * 
	 * @param p
	 */
	public void pressKey_down(boolean p) {
		press_down = p;
	}

	/**
	 * Establece si se ha presionado la tecla de la derecha
	 * 
	 * @param p
	 */
	public void pressKey_right(boolean p) {
		press_right = p;
	}

	/**
	 * Establece si se ha presionado la tecla de la izquierda
	 * 
	 * @param p
	 */
	public void pressKey_left(boolean p) {
		press_left = p;
	}

	/**
	 * Establece si se ha presionado la tecla z
	 * 
	 * @param p
	 */
	public void pressKey_z(boolean p) {
		this.press_z = p;
	}

	/* ################## CONSULTAS ######################## */
	
	/**
	 * Retorna si la ejecucion del juego logico ha finalizado.
	 * 
	 * @return true si la ejecucion del juego logico ha finalizado; false en otro caso
	 */
	public boolean isFinalizado() {
		return juegoFinalizado;
	}

	public boolean isJuegoReintentar() {
		return juegoReintentar;
	}

	/**
	 * Retorna si se ha ganado el juego.
	 * 
	 * @return true si se ha ganado el juego; false en otro caso
	 */
	public boolean isWin() {
		return juegoGanado;
	}

	/**
	 * Retorna si se ha perdido el juego. Esto puede pasar si Mario tiene 0 vidas.
	 * 
	 * @return true si se ha perdido el juego; false en otro caso
	 */
	public boolean isLost() {
		return juegoPerdido;
	}

	/**
	 * Retorna si se ha presioando la tecla z
	 * 
	 * @return true si se ha presioando la tecla z; false en otro caso
	 */
	public boolean isPress_z() {
		return press_z;
	}

	/**
	 * Retorna si se ha presioando la tecla de arriba
	 * 
	 * @return true si se ha presioando la tecla de arriba; false en otro caso
	 */
	public boolean isPress_up() {
		return press_up;
	}

	/**
	 * Retorna si se ha presioando la tecla de abajo
	 * 
	 * @return true si se ha presioando la tecla de abajo; false en otro caso
	 */
	public boolean isPress_down() {
		return press_down;
	}

	/**
	 * Retorna si se ha presioando la tecla de la derecha
	 * 
	 * @return true si se ha presioando la tecla de la derecha; false en otro caso
	 */
	public boolean isPress_right() {
		return press_right;
	}

	/**
	 * Retorna si se ha presioando la tecla de la izquierda
	 * 
	 * @return true si se ha presioando la tecla de la izquierda; false en otro caso
	 */
	public boolean isPress_left() {
		return press_left;
	}

	/**
	 * Retorna a Mario, contenido en el juego logico.
	 * 
	 * @return a Mario.
	 */
	public Mario getMario() {
		return mario;
	}

	/**
	 * Retorna el Mapa Visible contenido en el juego logico.
	 * 
	 * @return el Mapa Visible
	 */
	public MapaVisible getMapaVisible() {
		return mapaV;
	}

	/* ################## METODOS Y CLASES PRIVADAS ######################## */

	/* Hilo para realizar movimientos de Mario */
	private class hiloMario extends Thread {
		protected boolean continuo; // bandera que me permite cortar el hilo en ejecucion.

		public hiloMario() {
			super("1");
			continuo = true;
		}

		public void run() {
			
			juegoFinalizado = false;
			juegoGanado = false;
			juegoPerdido = false;
			while (continuo) {
				iterarMario();
				try {
					sleep(mario.getVelMov());
				} catch (InterruptedException e) {
				}
			}
		}

		/*
		 * metodo que permite parar la ejecucion del hilo. Cuando se invoca a este metodo entonces
		 * se finaliza la ejecucion del juego logico
		 */
		public void pararHilo() {
			continuo = false;
			juegoFinalizado = true;
		}

		/*
		 * realiza una iteracion del mario.
		 */
		private void iterarMario() {
			if (!mario.isDead()) { // si Mario esta vivo entonces realizo una iteracion
				mario.reestablecerMario();
				if (isPress_up() && isPress_right()) {
					mario.saltar(Constantes.dir_right);
				} else if (isPress_up() && isPress_left()) {
					mario.saltar(Constantes.dir_left);
				} else if (isPress_up()) {
					mario.saltar(Constantes.dir_top);
				} else if (isPress_right()) {
					mario.cambiarMovimiento(Constantes.dir_right);
				} else if (isPress_left()) {
					mario.cambiarMovimiento(Constantes.dir_left);
				} else {
					if (mario.getDirActual() == Constantes.dir_right)
						mario.cambiarMovimiento(Constantes.dir_nada_right);
					else if (mario.getDirActual()==Constantes.dir_left)
						mario.cambiarMovimiento(Constantes.dir_nada_left);
				}

				if (isPress_z())
					mario.setVelMov(5);
				else
					mario.setVelMov(7);

				mario.realizarMovimiento();
				dx = mario.getX() - mapaV.getInicio() - ((mapaV.getFin() - mapaV.getInicio()) / 2) + Constantes.sizeCelda;
				while (dx > 0) {
					mapaV.moverMapaV();
					dx--;
				}
			} else if (mario.getVidas()==1){
				/*
				 * si esta muerto y ademas no tiene mas vidas entonces concluyo la ejecucion del hilo, y establesco la situacion
				 * del juego como 'perdio=true'.
				 */
				
				juegoPerdido =true;
				pararHilo();
			}
			else{
				/* en otro caso, osea que mario esta muerto, pero tiene mas vidas... reinicio el juego le resto una vida */
				juegoReintentar=true;
				pararHilo();
			}

			/*
			 * si Mario gano el juego, entonces concluyo la ejecucion del juego logico y establesco
			 * la situacion del juego como "gano=true"
			 */
			if (mario.isWin()) {
				juegoGanado = true;
				pararHilo();
			}

		}

	}
	private class hiloObjetos extends Thread{
		private boolean continuo;
		public hiloObjetos (){
			super ("enemigos");
			continuo = true;
		}
		public void run(){
			while (continuo)
			{
				iterarEnemigos();
				iterarPowerups();
				iterarProyectiles();
				try {
					sleep(8);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		public void parar(){
			continuo = false;
		}
		public void iterarEnemigos (){
			mapaV.moverEnemigos();
		}
		public void iterarPowerups(){
			mapaV.moverPowerUps();
		}
		public void iterarProyectiles(){
			mapaV.moverProyectiles();
		}
	}
}
